﻿/*
 * 
 * Author: Martin Paulík
 * 
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.XPath;
using Microsoft.Xml.XQuery;
using System.Windows.Forms;

namespace PB138___projekt
{
    /// <summary>
    /// Class for working with XML document. 
    /// </summary>
    class XML
    {
        private XmlDocument doc;
        public XML()
        {
            doc = new XmlDocument();
            doc.Load("videos.xml");
        }

        /// <summary>
        /// Insert media to xml document. 
        /// </summary>
        /// <param name="id">ID of category</param>
        /// <param name="name">Name of media</param>
        /// <param name="author">Author of media</param>
        /// <param name="items">List of items - films or musics</param>
        /// <returns>True if done success, otherwise false.</returns>
        public bool insert(int id, string name, string author, List<Item> items)
        {
            try
            {
                XmlNode medium = createMedium(id.ToString(), name, author, items);
                XmlNode dvds = doc.SelectSingleNode("videos/dvds");
                dvds.AppendChild(medium);
                doc.Save("videos.xml");
                doc = new XmlDocument();
                doc.Load("videos.xml");
            }
            catch (System.Xml.XmlException e)
            {
                MessageBox.Show("Error - file is not saved", "ERROR", System.Windows.Forms.MessageBoxButtons.OK,
                       System.Windows.Forms.MessageBoxIcon.Error);
                return false;
            }
            catch
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// Save, close and load.
        /// </summary>
        private void refresh()
        {
            doc.Save("videos.xml");
            doc = new XmlDocument();
            doc.Load("videos.xml");
        }

        /// <summary>
        /// Create new XmlNode new category, append to categories and save. 
        /// </summary>
        /// <param name="name">Name of category</param>
        /// <param name="idCat">ID of category</param>
        /// <returns>True if done success, otherwise false.</returns>
        public bool insertCategory(string name, string idCat)
        {
            try
            {
                XmlNode newCategory = doc.CreateElement("category");
                XmlAttribute id = doc.CreateAttribute("id");
                if (idCat == null)
                {
                    id.Value = conversion(doc.SelectSingleNode("videos/max_category_count").Attributes["value"].Value);
                    changeCategoryMaxCount(id.Value);
                }
                else
                {
                    id.Value = idCat;
                }
                XmlAttribute nameCategory = doc.CreateAttribute("name");
                nameCategory.Value = name;
                newCategory.Attributes.Append(id);
                newCategory.Attributes.Append(nameCategory);
                XmlNode categories = doc.SelectSingleNode("videos/categories");
                categories.AppendChild(newCategory);
                refresh();
            }
            catch { return false; }
            return true;

        }

        /// <summary>
        /// Rename category on newName. 
        /// </summary>
        /// <param name="removeName">Old name of category</param>
        /// <param name="newName">New name of category</param>
        /// <returns>True if done success, otherwise false.</returns>
        public bool deleteCategory(string removeName, string newName)
        {
            try
            {
                XmlNode oldCategory = getCategory(removeName);
                string idOldCategory = oldCategory.Attributes["id"].Value;
                XmlNode categories = doc.SelectSingleNode("videos/categories");
                categories.RemoveChild(oldCategory);
                refresh();
                insertCategory(newName, idOldCategory);
            }
            catch { return false; }
            return true;


        }

        /// <summary>
        /// Delete category from xml document and all medias which were in category. 
        /// </summary>
        /// <param name="removeName">Name of category which will be removed</param>
        /// <returns>True if done succesfully, otherwise false. </returns>
        public bool deleteCategory(string removeName)
        {
            try
            {
                XmlNode oldCategory = getCategory(removeName);
                string idOldCategory = oldCategory.Attributes["id"].Value;
                XmlNode categories = doc.SelectSingleNode("videos/categories");
                categories.RemoveChild(oldCategory);
                XmlNode dvds = doc.SelectSingleNode("videos/dvds");
                XmlNodeList allDvds = doc.SelectNodes("videos/dvds/dvd[@category='" + idOldCategory + "']");
                foreach (XmlNode dvd in allDvds)
                {
                    dvds.RemoveChild(dvd);
                }
                refresh();
            }
            catch { return false; }
            return true; 
        }

        /// <summary>
        /// Get Xml Node category by name.
        /// </summary>
        /// <param name="name">Name of category</param>
        /// <returns>If category is found, return XmlNode founded category, otherwise null.</returns>
        public XmlNode getCategory(string name)
        {
            string xpath = "videos/categories/category[@name='" + name + "']";
            XmlNode category = doc.SelectSingleNode(xpath);
            return category;
        }

        /// <summary>
        /// Get Xml Node category by id.
        /// </summary>
        /// <param name="index">ID of category</param>
        /// <returns>If category is found, return XmlNode founded category, otherwise null.</returns>
        public XmlNode getCategory(int index)
        {
            string xpath = "videos/categories/category[@id='" + index.ToString() + "']";
            XmlNode category = doc.SelectSingleNode(xpath);
            return category;
        }

        /// <summary>
        /// Delete DVD.
        /// </summary>
        /// <param name="id">ID of DVD</param>
        /// <returns>True if done success, otherwise false.</returns>
        public bool deleteDVD(string id)
        {
            try
            {
                XmlNode dvd = doc.SelectSingleNode("videos/dvds/dvd[@id='" + id + "']");
                XmlNode dvds = doc.SelectSingleNode("videos/dvds");
                dvds.RemoveChild(dvd);
                refresh();
            }
            catch { return false; }
            return true;
        }

        /// <summary>
        /// Create node medium.
        /// </summary>
        /// <param name="id">ID of category</param>
        /// <param name="name">Name of medium</param>
        /// <param name="author">Author of medium</param>
        /// <param name="items">List of items of medium</param>
        /// <returns>Creating node medium, otherwise null.</returns>
        private XmlNode createMedium(string id,string name, string author, List<Item> items)
        {
            XmlNode medium = doc.CreateElement("dvd");
            XmlAttribute atributCategory = doc.CreateAttribute("category");
            XmlAttribute atributID = doc.CreateAttribute("id");
            
            atributID.Value = conversion(doc.SelectSingleNode("videos/max_dvd_count").Attributes["value"].Value);
            changeDVDMaxCount(atributID.Value);
            atributCategory.Value = id;
            medium.Attributes.Append(atributID);
            medium.Attributes.Append(atributCategory);
            
            XmlNode nameDVD = doc.CreateElement("name");
            XmlAttribute valueName = doc.CreateAttribute("value");
            XmlNode authorDVD = doc.CreateElement("author");
            XmlAttribute value = doc.CreateAttribute("value");
            valueName.Value = name;
            nameDVD.Attributes.Append(valueName);
            value.Value = author;
            authorDVD.Attributes.Append(value);
            medium.AppendChild(nameDVD);
            medium.AppendChild(authorDVD);

            foreach (Item it in items)
            {
                XmlNode item = doc.CreateElement("item");
                XmlAttribute nameItem = doc.CreateAttribute("name");
                nameItem.Value = it.Name;
                item.Attributes.Append(nameItem);
                if (it.Year != "")
                {
                    XmlAttribute yearItem = doc.CreateAttribute("year");
                    yearItem.Value = it.Year;
                    item.Attributes.Append(yearItem);
                }
                if (it.Duration != "")
                {
                    XmlAttribute durationItem = doc.CreateAttribute("duration");
                    durationItem.Value = it.Duration;
                    item.Attributes.Append(durationItem);
                }
                medium.AppendChild(item);

            }
            return medium;
        }

        /// <summary>
        /// Conversion from string to int, plus 1 and conversion back to string. 
        /// </summary>
        /// <param name="value">Value which contain old id.</param>
        private string conversion(string value)
        {
            int id = Convert.ToInt32(value);
            id++;
            return (id.ToString());
        }

        /// <summary>
        /// Increase tag videos/max_dvd_count by param value
        /// </summary>
        /// <param name="value">Value which contain new id_max of dvd.</param>
        private void changeDVDMaxCount(string value) 
        {
            XmlNode maxCount = doc.SelectSingleNode("videos/max_dvd_count");
            maxCount.Attributes["value"].Value = value;
        }

        /// <summary>
        /// Increase tag videos/max_category_count by param value
        /// </summary>
        /// <param name="value">Value which contain new id_max of category.</param>
        private void changeCategoryMaxCount(string value)
        {
            XmlNode maxCount = doc.SelectSingleNode("videos/max_category_count");
            maxCount.Attributes["value"].Value = value;
        }

        /// <summary>
        /// Searching media by name of film. Use XQuery. It write result to output.xml. 
        /// </summary>        
        /// <param name="nameOfFilm">Name of film which will be searching.</param>
        /// <returns>If not found, return false, otherwise true.</returns>
        public bool searchMedium(string nameOfFilm) 
        {
            //vyhleda a ulozi ve formatu xml do stringu
            XQueryNavigatorCollection col = new XQueryNavigatorCollection();
            col.AddNavigator("./videos.xml", "doc");
            string query = "<media>" +
            " { for $dvd in document(\"doc\")/videos/dvds/dvd" +
            " for $film in $dvd/item" +
            " where $film/@name =\"" + nameOfFilm + "\"" +
            " return $dvd " +
            "}</media>";
            XQueryExpression xepr = new XQueryExpression(query);
            string result = (xepr.Execute(col)).ToXml();

            // zapis vysledku do output.xml
            XQueryNavigator nav1;
            nav1 = xepr.Execute(col);
            XmlDocument doc2 = new XmlDocument();
            doc2.LoadXml(nav1.ToXml());
            XmlTextWriter writer1 = new XmlTextWriter("output.xml", System.Text.Encoding.UTF8);
            doc2.Save(writer1);
            writer1.Close();
            if (result.Length > 15)
                return true;
            else
                return false;

        }

        /// <summary>
        /// Get list of categories. 
        /// </summary>
        public List<string> getCategories()
        {
            List<string> nameCategories = new List<string>();
            XmlNode categories = doc.SelectSingleNode("videos/categories");
            foreach (XmlNode category in categories.ChildNodes)
            {
                nameCategories.Add(category.Attributes["name"].Value);

            }
            return nameCategories;
        }

        /// <summary>
        /// Get list of media by name of category. 
        /// </summary>
        /// <param name="name">Name of category</param>
        public XmlNodeList getListMedia(string name)
        {
            XmlNode category = getCategory(name); 
            string xpath = "videos/dvds/dvd[@category='" + category.Attributes["id"].Value + "']";
            XmlNodeList listMedia = doc.SelectNodes(xpath);
            return listMedia;        

        }

    }

    /// <summary>
    /// Class which contain name, duration and year required for item
    /// </summary>
    class Item
    {
        string name;
        string duration;
        string year;
        public Item(string n, string d, string y)
        {
            this.name = n;
            this.duration = d;
            this.year = y;
        }
        public string Name
        {
            get { return name; }
            set { this.name = value; }
        }
        public string Duration
        {
            get { return duration; }
            set { this.duration = value; }
        }
        public string Year
        {
            get { return year; }
            set { this.year = value; }
        }
    }

}
